﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
using TrinityAPI.Base.Models;

namespace TrinityAPI.Base.Tools
{
    public class Utils
    {
        /// <summary>
        /// @参数正则表达式
        /// </summary>
        public static string ParamRegexPattern = @"@([a-zA-Z0-9_]+(?:\[(?:[0-9]+|n|N)\])?(?:\.\!?[a-zA-Z0-9_]+(?:\[(?:[0-9]+|n|N)\])?)*)((?<!\])\(([^\(\)]*?)\))?(?:\.([a-zA-Z0-9_]+)\(([^\(\)]*?)\))*";

        /// <summary>
        /// 预留的SQL关键字
        /// </summary>
        public static List<string> SqlKeywords = new List<string>() { "if", "from", "where", "identity", "sql" };

        /// <summary>
        /// 预留的数据集合键值
        /// </summary>
        public static List<string> DataIdKeywords = new List<string>() { "query", "querystring", "request", "requeststring", "requesturi", "header", "httpmethod", "linqs", "constant", "runtimedata", "error" };

        /// <summary>
        /// 根据web目录路径获取系统目录路径
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string GetMapPath(string path)
        {
            if (!string.IsNullOrWhiteSpace(path))
            {
                try
                {
                    if (path.IndexOf(":\\") > -1)
                        return path;
                    return System.Web.Hosting.HostingEnvironment.MapPath(path);
                }
                catch
                { }
            }
            return string.Empty;
        }
        /// <summary>
        /// 路径拼接
        /// </summary>
        /// <param name="paths">要拼接的路径集合</param>
        /// <returns></returns>
        public static string PathCombine(params string[] paths)
        {
            return PathCombine(true, paths);
        }

        /// <summary>
        /// 路径拼接
        /// </summary>
        /// <param name="isUrl">是否是网页路径</param>
        /// <param name="paths">要拼接的路径集合</param>
        /// <returns></returns>
        public static string PathCombine(bool isUrl, params string[] paths)
        {
            List<string> list = new List<string>();
            foreach (string path in paths)
            {
                if (!string.IsNullOrWhiteSpace(path))
                {
                    if (list.Count == 0)
                        list.Add(path);
                    else
                        list.Add(path.Trim('/').Trim('\\'));
                }
            }
            if (list.Count > 0)
            {
                if (isUrl)
                    return System.IO.Path.Combine(list.ToArray()).Replace("\\", "/");
                else
                    return System.IO.Path.Combine(list.ToArray());
            }
            return string.Empty;

        }

        /// <summary>  
        /// 对象序列化成 XML String  
        /// </summary>  
        public static string XmlSerialize<T>(T obj)
        {
            System.Xml.Serialization.XmlSerializerNamespaces xmlnsEmpty = new System.Xml.Serialization.XmlSerializerNamespaces();
            xmlnsEmpty.Add("", "");//去除默认命名空间 xmlns:xsd和xmlns:xsi

            System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
            System.IO.MemoryStream stream = new System.IO.MemoryStream();
            System.Xml.XmlWriterSettings setting = new System.Xml.XmlWriterSettings();
            setting.Encoding = new UTF8Encoding(false);
            setting.Indent = true;
            using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(stream, setting))
            {
                xs.Serialize(writer, obj, xmlnsEmpty);
            }
            return Encoding.UTF8.GetString(stream.ToArray());
        }

        /// <summary>  
        /// XML String 反序列化成对象  
        /// </summary>  
        public static T XmlDeserialize<T>(string xmlString)
        {
            T t = default(T);
            System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
            using (System.IO.Stream xmlStream = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(xmlString)))
            {
                using (System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(xmlStream))
                {
                    Object obj = xmlSerializer.Deserialize(xmlReader);
                    t = (T)obj;
                }
            }
            return t;
        }

        /// <summary>
        /// 发送邮件
        /// </summary>
        /// <param name="subject">邮件主体</param>
        /// <param name="message">邮件内容</param>
        /// <param name="mailTo">接收人</param>
        /// <param name="mailFrom">发送人</param>
        /// <param name="smtpHost">邮件服务地址</param>
        /// <param name="userName">邮件服务账户名</param>
        /// <param name="password">邮件服务密码</param>
        /// <param name="domain">邮件服务域</param>
        /// <param name="isBodyHtml">html格式</param>
        /// <param name="cc">抄送</param>
        /// <param name="bcc">密抄</param>
        /// <param name="attachments">附件</param>
        /// <param name="isAsync">是否异步发送</param>
        public static void SendEmail(string subject, string message, string mailTo, string mailFrom, string smtpHost, string userName = null, string password = null, string domain = null, bool isBodyHtml = true, string cc = null, string bcc = null, Dictionary<string, byte[]> attachments = null, bool isAsync = false)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(subject))
                    throw new ArgumentNullException("subject");
                if (string.IsNullOrWhiteSpace(message))
                    throw new ArgumentNullException("message");
                if (string.IsNullOrWhiteSpace(mailTo))
                    throw new ArgumentNullException("mailTo");
                if (string.IsNullOrWhiteSpace(mailFrom))
                    throw new ArgumentNullException("mailFrom");
                if (string.IsNullOrWhiteSpace(smtpHost))
                    throw new ArgumentNullException("smtpHost");

                string body = message;

                System.Net.Mail.MailMessage mail = new System.Net.Mail.MailMessage();

                mail.From = new System.Net.Mail.MailAddress(mailFrom);

                if (!string.IsNullOrEmpty(mailTo))
                {
                    string[] arrMailTos = mailTo.Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries);

                    foreach (string rec in arrMailTos)
                    {
                        if (!string.IsNullOrEmpty(rec))
                        {
                            mail.To.Add(rec);
                        }
                    }
                }

                if (!string.IsNullOrEmpty(cc))
                {
                    string[] arrCcs = cc.Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries);

                    foreach (string rec in arrCcs)
                    {
                        if (!string.IsNullOrEmpty(rec))
                        {
                            mail.CC.Add(rec);
                        }
                    }
                }

                if (!string.IsNullOrEmpty(bcc))
                {
                    string[] arrbccs = bcc.Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries);

                    foreach (string rec in arrbccs)
                    {
                        if (!string.IsNullOrEmpty(rec))
                        {
                            mail.Bcc.Add(rec);
                        }
                    }
                }


                mail.IsBodyHtml = isBodyHtml;
                mail.Subject = subject;
                mail.Body = (body ?? "").Replace(Environment.NewLine, "<br/>");

                if (attachments != null && attachments.Count > 0)
                {
                    foreach (var attachment in attachments)
                    {
                        var att = new System.Net.Mail.Attachment(new System.IO.MemoryStream(attachment.Value), attachment.Key);
                        att.ContentId = attachment.Key;
                        mail.Attachments.Add(att);
                    }
                }

                System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient(smtpHost);
                if (!string.IsNullOrWhiteSpace(userName) && !string.IsNullOrWhiteSpace(password))
                {
                    if (string.IsNullOrWhiteSpace(domain))
                        client.Credentials = new System.Net.NetworkCredential(userName, password);
                    else
                        client.Credentials = new System.Net.NetworkCredential(userName, password, domain);
                }

                if (isAsync)
                {
                    client.SendAsync(mail, null);
                }
                else
                {
                    client.Send(mail);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Error("Send email error", ex);
            }
        }

        /// <summary>
        /// 加载文本文件
        /// </summary>
        /// <param name="path">文档路径</param>
        /// <returns></returns>
        public static string LoadTextFile(string path)
        {
            try
            {
                if (!System.IO.File.Exists(path))
                    return string.Empty;
                return System.IO.File.ReadAllText(path);
            }
            catch
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// 加载文本文件
        /// </summary>
        /// <param name="path">文档路径</param>
        /// <param name="lastWriteTime">文档最后修改时间</param>
        /// <returns></returns>
        public static string LoadTextFile(string path, out DateTime lastWriteTime)
        {
            try
            {
                if (!System.IO.File.Exists(path))
                {
                    lastWriteTime = DateTime.MinValue;
                    return string.Empty;
                }
                lastWriteTime = System.IO.File.GetLastWriteTime(path);
                return System.IO.File.ReadAllText(path);
            }
            catch
            {
                lastWriteTime = DateTime.MinValue;
                return string.Empty;
            }
        }
        
        /// <summary>
        ///  以只读方式打开文件， 获取指定文件的字节数组
        /// </summary>
        /// <param name="filepath"></param>
        /// <returns></returns>
        public static byte[] GetBytesFromFile(string filepath)
        {
            if (!System.IO.File.Exists(filepath))
            {
                return null;
            }

            var fi = new System.IO.FileInfo(filepath);
            long len = fi.Length;

            using (var fs = new System.IO.FileStream(filepath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
            {
                byte[] buffer = new byte[len];
                fs.Read(buffer, 0, (int)len);
                fs.Close();

                return buffer;
            }
        }

        /// <summary>
        /// 生成MD5哈希码
        /// </summary>
        /// <param name="input"></param>
        /// <param name="encoding"></param>
        /// <param name="isUpper"></param>
        /// <param name="isBase64"></param>
        /// <returns></returns>
        public static string MD5(string input, Encoding encoding = null, bool isUpper = false, bool isBase64 = false)
        {
            StringBuilder sb = new StringBuilder();
            if (input != null)
            {
                if (encoding == null)
                    encoding = Encoding.UTF8;
                byte[] bytes = encoding.GetBytes(input);
                using (var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider())
                {
                    byte[] result = md5.ComputeHash(bytes);
                    if (isBase64)
                        sb.Append(Convert.ToBase64String(result));
                    else
                        foreach (byte b in result)
                            sb.AppendFormat(isUpper ? "{0:X2}" : "{0:x2}", b);
                }
            }
            return sb.ToString();
        }

        /// <summary>
        /// 生成SHA1哈希码
        /// </summary>
        /// <param name="input"></param>
        /// <param name="encoding"></param>
        /// <param name="isUpper"></param>
        /// <param name="isBase64"></param>
        /// <returns></returns>
        public static string GetSha1Hash(string input, Encoding encoding = null, bool isUpper = false, bool isBase64 = false)
        {
            StringBuilder sb = new StringBuilder();
            if (input != null)
            {
                if (encoding == null)
                    encoding = Encoding.UTF8;
                byte[] bytes = encoding.GetBytes(input);
                using (System.Security.Cryptography.SHA1 sha1 = new System.Security.Cryptography.SHA1CryptoServiceProvider())
                {
                    byte[] result = sha1.ComputeHash(bytes);
                    if (isBase64)
                        sb.Append(Convert.ToBase64String(result));
                    else
                        foreach (byte b in result)
                            sb.AppendFormat(isUpper ? "{0:X2}" : "{0:x2}", b);
                }
            }
            return sb.ToString();
        }

        /// <summary>
        /// 获取请求的编码类型
        /// </summary>
        /// <param name="ctype"></param>
        /// <returns></returns>
        public static string GetRequestCharset(string ctype = null)
        {
            string charset = "utf-8";
            if (!string.IsNullOrWhiteSpace(ctype))
            {
                string[] entires = ctype.Split(';');
                foreach (string entry in entires)
                {
                    string _entry = entry.Trim();
                    if (_entry.StartsWith("charset"))
                    {
                        string[] pair = _entry.Split('=');
                        if (pair.Length == 2)
                        {
                            if (!string.IsNullOrWhiteSpace(pair[1]))
                            {
                                charset = pair[1].Trim();
                            }
                        }
                        break;
                    }
                }
            }
            return charset;
        }

        public static System.Data.IsolationLevel? GetIsolationLevel(string isolation)
        {
            if (!string.IsNullOrWhiteSpace(isolation))
            {
                switch (isolation.ToLower())
                {
                    case "readuncommitted":
                        return System.Data.IsolationLevel.ReadUncommitted;
                    case "readcommitted":
                        return System.Data.IsolationLevel.ReadCommitted;
                    case "repeatableread":
                        return System.Data.IsolationLevel.RepeatableRead;
                    case "serializable":
                        return System.Data.IsolationLevel.Serializable;
                }
            }
            return null;
        }

        public static Function GetFunction(string path, JObject dataset)
        {
            if (string.IsNullOrWhiteSpace(path))
                return null;

            Function function = null;
            string pattern = @"^([a-zA-Z0-9_]+)\(([^\(\)]*?)\)$";
            var matchs = Regex.Matches(path, pattern);
            if (matchs.Count == 1)
            {
                Match match = matchs[0];
                function = new Function(match.Groups[1].Value, match.Groups[2].Value, dataset);
            }
            
            return function;
        }

        public static List<Function> GetFunctions(ref string path, JObject dataset)
        {
            if (string.IsNullOrWhiteSpace(path))
                return null;

            List<Function> functions = null;
            string pattern = @"^([a-zA-Z0-9_]+(?:\[(?:[0-9]+|n|N)\])?(?:\.\!?[a-zA-Z0-9_]+(?:\[(?:[0-9]+|n|N)\])?)*\.)?([a-zA-Z0-9_]+)\(([^\(\)]*?)\)(?:\.([a-zA-Z0-9_]+)\(([^\(\)]*?)\))*$";
            var matchs = Regex.Matches(path, pattern);
            if (matchs.Count == 1)
            {
                functions = new List<Function>();
                Match match = matchs[0];
                functions.Add(new Function(match.Groups[2].Value, match.Groups[3].Value, dataset));
                for (int i = 0; i < match.Groups[4].Captures.Count; i++)
                    functions.Add(new Function(match.Groups[4].Captures[i].Value, match.Groups[5].Captures[i].Value, dataset));
                path = (match.Groups[1].Value ?? string.Empty).TrimEnd('.');
            }
            
            return functions;
        }

        public static Parameter GetParameter(string path, JObject dataset)
        {
            if (string.IsNullOrWhiteSpace(path))
                return null;

            List<Function> functions = GetFunctions(ref path, dataset);

            JToken value = string.IsNullOrWhiteSpace(path) && functions != null ? JValue.CreateUndefined() : Utils.GetValueByPath(path, dataset);
            return new Parameter(path, value, functions);
        }

        public static Parameter GetParameter(string path, JObject dataset, List<KeyValuePair<string, string>> funcs)
        {
            List<Function> functions = null;
            if (funcs != null && funcs.Count > 0)
            {
                functions = new List<Function>();
                for (int i = 0; i < funcs.Count; i++)
                    functions.Add(new Function(funcs[i].Key, funcs[i].Value, dataset));
            }

            JToken value = string.IsNullOrWhiteSpace(path) && functions != null ? JValue.CreateUndefined() : Utils.GetValueByPath(path, dataset);
            return new Parameter(path, value, functions);
        }

        public static JToken TryConvertToInt(JToken value, int defaultValue = 0)
        {
            if (value == null)
            {
                return defaultValue;
            }
            if (value.Type == JTokenType.String)
            {
                decimal outvalue = 0;
                if (decimal.TryParse(value.ToString(), out outvalue))
                    return (int)outvalue;
                else
                    return defaultValue;
            }
            else if (value.Type == JTokenType.Integer || value.Type == JTokenType.Float)
            {
                return (int)DecimalHelper.ToDecimal(((JValue)value).Value);
            }
            else if (value.Type == JTokenType.Boolean)
            {
                return value.Value<bool>() ? 1 : 0;
            }
            else if (value.Type == JTokenType.Array)
            {
                JArray arr = new JArray();
                foreach (var item in value as JArray)
                {
                    arr.Add(TryConvertToInt(item, defaultValue));
                }
                return arr;
            }

            return defaultValue;
        }

        public static JToken TryConvertToDecimal(JToken value, decimal defaultValue = 0)
        {
            if (value == null)
            {
                return defaultValue;
            }
            if (value.Type == JTokenType.String)
            {
                decimal outvalue = 0;
                if (decimal.TryParse(value.ToString(), out outvalue))
                    return outvalue;
                else
                    return defaultValue;
            }
            else if (value.Type == JTokenType.Integer || value.Type == JTokenType.Float)
            {
                return value;
            }
            else if (value.Type == JTokenType.Boolean)
            {
                return value.Value<bool>() ? 1 : 0;
            }
            else if (value.Type == JTokenType.Array)
            {
                JArray arr = new JArray();
                foreach (var item in value as JArray)
                {
                    arr.Add(TryConvertToDecimal(item, defaultValue));
                }
                return arr;
            }

            return defaultValue;
        }

        public static JArray SimpleArrayToObjectArray(JToken value, string field)
        {
            if (value == null || value.Type != JTokenType.Array)
                return null;
            if (string.IsNullOrWhiteSpace(field))
                field = "value";
            var array = new JArray();
            foreach (var item in value as JArray)
            {
                if (item is JValue)
                    array.Add(new JObject() { { field, item } });
            }
            return array;
        }

        public static JArray ObjectArrayToSimpleArray(JToken value, string field)
        {
            if (value == null || value.Type != JTokenType.Array)
                return null;
            var array = new JArray();
            foreach (var item in value as JArray)
            {
                var objectItem = item as JObject;
                if (objectItem != null && objectItem.HasValues)
                {
                    var itemValue = string.IsNullOrWhiteSpace(field) ? objectItem.GetJsonObjectValue((objectItem.First as JProperty).Name) : objectItem.GetJsonObjectValue(field);
                    array.Add(itemValue ?? JValue.CreateNull());
                }
            }
            return array;
        }

        public static JToken SumTotal(JToken value, string field)
        {
            if (string.IsNullOrWhiteSpace(field) || value == null || value.Type != JTokenType.Array)
            {
                return 0;
            }
            decimal total = 0;
            foreach (var item in value as JArray)
            {
                var val = TryConvertToDecimal(item[field]);
                if (val.Type == JTokenType.Array)
                    foreach (var i in val)
                        total += i.Value<decimal>();
                else
                    total += val.Value<decimal>();
            }
            return total;
        }

        public static JToken SelectSomeFields(JToken value, string[] fields)
        {
            if (fields == null || fields.Length == 0 || value == null || (value.Type != JTokenType.Object && value.Type != JTokenType.Array))
            {
                return value;
            }
            JArray array = value.Type == JTokenType.Object ? new JArray() { value as JObject } : value as JArray;
            JArray result = new JArray();
            foreach (var item in array)
            {
                if (item.Type != JTokenType.Object)
                    continue;
                var itemObject =  item as JObject;

                var obj = new JObject();
                foreach (string key in fields)
                {
                    if (itemObject.ContainsProperty(key))
                    {
                        obj.SetJsonObjectValue(key, itemObject.GetJsonObjectValue(key));
                    }
                    else
                    {
                        obj.SetJsonObjectValue(key, null);
                    }
                }
                result.Add(obj);
            }
            return value.Type == JTokenType.Object ? result[0] : result;
        }

        public static JToken Filter(JToken value, Func<JToken, bool> func)
        {
            if (value == null || value.Type != JTokenType.Array)
            {
                return value;
            }
            JArray array = value as JArray;
            JArray result = new JArray();
            foreach (var item in array)
            {
                if (func.Invoke(item))
                    result.Add(item);
            }
            return result;
        }

        public static bool FilterComparator(JObject obj, string field, JToken value)
        {
            if (string.IsNullOrWhiteSpace(field))
                return false;
            string Operator = "=";
            string[] Operators = new string[] { "=", "!=", ">", "<", ">=", "<=", "_*", "*_", "**", "!*" };
            if (field.Length > 2)
            {
                string OperatorStr = field.Substring(field.Length - 2);
                if (Operators.Contains(OperatorStr))
                {
                    field = field.Substring(0, field.Length - 2);
                    Operator = OperatorStr;
                }
                else
                {
                    OperatorStr = field.Substring(field.Length - 1);
                    if (Operators.Contains(OperatorStr))
                    {
                        field = field.Substring(0, field.Length - 1);
                        Operator = OperatorStr;
                    }
                }
            }
            else if (field.Length > 1)
            {
                string OperatorStr = field.Substring(field.Length - 1);
                if (Operators.Contains(OperatorStr))
                {
                    field = field.Substring(0, field.Length - 1);
                    Operator = OperatorStr;
                }
            }

            var itemValue = obj.GetJsonObjectValue(field);
            if (!(value is JValue) || !(itemValue is JValue))
                return false;

            try
            {
                JValue SourceValue = (JValue)itemValue;
                JValue TargetValue = (JValue)value;
                switch (Operator)
                {
                    case "=":
                        return SourceValue.Equals(TargetValue);
                    case "!=":
                        return !SourceValue.Equals(TargetValue);
                    case ">":
                        return SourceValue.CompareTo(TargetValue) > 0;
                    case ">=":
                        return SourceValue.CompareTo(TargetValue) >= 0;
                    case "<":
                        return SourceValue.CompareTo(TargetValue) < 0;
                    case "<=":
                        return SourceValue.CompareTo(TargetValue) <= 0;
                    case "_*":
                        return SourceValue.Type == JTokenType.String && TargetValue.Type == JTokenType.String && SourceValue.Value<string>().StartsWith(TargetValue.Value<string>());
                    case "*_":
                        return SourceValue.Type == JTokenType.String && TargetValue.Type == JTokenType.String && SourceValue.Value<string>().EndsWith(TargetValue.Value<string>());
                    case "**":
                        return SourceValue.Type == JTokenType.String && TargetValue.Type == JTokenType.String && SourceValue.Value<string>().Contains(TargetValue.Value<string>());
                    case "!*":
                        return SourceValue.Type == JTokenType.String && TargetValue.Type == JTokenType.String && !SourceValue.Value<string>().Contains(TargetValue.Value<string>());
                    default:
                        return false;
                }
            }
            catch
            {
                return false;
            }
        }

        public static string Base64Conversion(string input, bool toBase64 = true, Encoding encoding = null)
        {
            if (input != null)
            {
                if (encoding == null)
                    encoding = Encoding.UTF8;
                if (toBase64)
                    return Convert.ToBase64String(encoding.GetBytes(input));
                else
                {
                    try
                    {
                        return encoding.GetString(Convert.FromBase64String(input));
                    }
                    catch (Exception ex)
                    {
                        LogHelper.Warn("Base64Conversion error", ex);
                        return null;
                    }
                }
            }
            return null;
        }

        public static JToken XMLConversion(JToken input, bool toXML = true)
        {
            if (input != null)
            {
                if (toXML)
                {
                    try
                    {
                        using (var stringWriter = new System.IO.StringWriter())
                        {
                            using (var xmlTextWriter = System.Xml.XmlWriter.Create(stringWriter))
                            {
                                var xmlDoc = Newtonsoft.Json.JsonConvert.DeserializeXmlNode(input.ToString());
                                xmlDoc.WriteTo(xmlTextWriter);
                                xmlTextWriter.Flush();
                                return stringWriter.GetStringBuilder().ToString();
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        LogHelper.Warn("To XML Error", ex);
                    }
                }
                else if (input.Type == JTokenType.String)
                {
                    try
                    {
                        var xmlDoc = new System.Xml.XmlDocument();
                        xmlDoc.LoadXml(input.ToString());
                        string json = Newtonsoft.Json.JsonConvert.SerializeXmlNode(xmlDoc, Newtonsoft.Json.Formatting.None);
                        return JObject.Parse(json);
                    }
                    catch (Exception ex)
                    {
                        LogHelper.Warn("From XML Error", ex);
                    }
                }
            }
            return null;
        }

        public static JToken CallJSFunction(string name, params JToken[] args)
        {
            if (name == null || APIContext.ScriptEngine == null || APIContext.ScriptEngine.Script == null || APIContext.ScriptEngine.Script[name] is Microsoft.ClearScript.Undefined)
                throw ExceptionHelper.GenException(ErrorCode.E000000007, name);
            int argType = 0;
            JArray argArrary = new JArray(args);
            if (args.Length > 0)
            {
                JToken lastArg = args.LastOrDefault();
                if (lastArg?.Type == JTokenType.String && lastArg.ToString().ToLower().StartsWith("argtype"))
                {
                    int.TryParse(lastArg.ToString().Substring(7), out argType);
                    argArrary.RemoveAt(args.Length - 1);
                }
            }
            List<object> paramList = new List<object>();
            object arguments = argType == 1 || argType == 2 ? argArrary : Utils.GetJSAarguments(argArrary);
            if (arguments is IList)
            {
                foreach (object arg in (IList)arguments)
                    paramList.Add(argType == 1 && arg is JToken ? ((JToken)arg).ToString(Newtonsoft.Json.Formatting.None) : arg);
            }
            try
            {
                if (argType == 1 && paramList.Count > 0)
                    return LinqHelper.ToJToken(APIContext.ScriptEngine.Evaluate(string.Format("{0}({1})", name, string.Join(",", paramList))));
                else
                    return LinqHelper.ToJToken(APIContext.ScriptEngine.Invoke(name, paramList.ToArray()));
            }
            catch (Exception ex)
            {
                if (ex.Message.StartsWith("@"))
                {
                    var msg = ex.Message.Split('\n').ToList();
                    if (msg.Count > 1)
                        throw ExceptionHelper.GenException(msg[0], msg.GetRange(1, msg.Count - 1).ToArray());
                    else
                        throw ExceptionHelper.GenException(msg[0]);
                }
                throw ex;
            }
        }

        public static JToken ConcatItems(string separator, JToken value, params JToken[] args)
        {
            if (args.Length == 0)
                return value;
            JToken result = null;
            if (value == null || value.Type == JTokenType.Null || value.Type == JTokenType.Undefined || value.Type == JTokenType.String)
            {
                if (separator == null)
                    separator = "";
                List<string> argList = new List<string>();
                if (value?.Type == JTokenType.String)
                    argList.Add(value.ToString());
                foreach (var arg in args)
                    if (arg != null && arg.Type != JTokenType.Null && arg.Type != JTokenType.Undefined)
                        argList.Add(arg.ToString());
                result = string.Join(separator, argList);
            }
            else if (value.Type == JTokenType.Array)
            {
                var newAarray = new JArray((JArray)value);
                foreach (var arg in args)
                {
                    if (arg?.Type != JTokenType.Array)
                    {
                        newAarray.Add(arg);
                        continue;
                    }
                    foreach (var item in (JArray)arg)
                        newAarray.Add(item);
                }
                result = newAarray;
            }
            return result;
        }

        public static JToken GetValueByPath(string path, JToken dataset, bool first = true)
        {
            if (string.IsNullOrWhiteSpace(path) || dataset == null || !dataset.HasValues)
                return null;

            JToken data = dataset;

            #region 解析相对路径参数
            if (first)
            {
                path = path.ToLower();
                if (path == "this" || path.StartsWith("this.") || path.StartsWith("this["))
                {
                    var runtimedata = RuntimeData.GetData(data as JObject);
                    if (runtimedata == null)
                        return null;
                    string cursor = runtimedata.Cursor;
                    if (string.IsNullOrWhiteSpace(cursor))
                        return null;

                    string newPath1 = cursor.EndsWith("]") ? cursor.Substring(0, cursor.LastIndexOf('[')) : cursor.Substring(0, cursor.LastIndexOf('.'));
                    if (newPath1.Length == 0)
                        return null;
                    if (path == "this")
                        path = newPath1;
                    else
                    {
                        string newPath2 = path.Substring(4);
                        if (newPath2.Length == 0)
                            return null;
                        path = newPath1 + newPath2;
                    }
                    data = runtimedata.Data;
                }
                else if (path.ToLower().StartsWith("parent.") || path.ToLower().StartsWith("parent["))
                {
                    var runtimedata = RuntimeData.GetData(data as JObject);
                    if (runtimedata == null)
                        return null;
                    string cursor = runtimedata.Cursor;
                    if (string.IsNullOrWhiteSpace(cursor))
                        return null;

                    int startIndex = 0;
                    int endIndex = 0;
                    var originPaths = path.Split('.');
                    var runtimePaths = cursor.Split(new char[] { '.', '[' }).ToList();
                    endIndex += runtimePaths.Last().Length + 1;
                    runtimePaths.RemoveAt(runtimePaths.Count - 1);
                    foreach (var p in originPaths)
                    {
                        if (p != "parent")
                            break;
                        startIndex += p.Length + 1;
                        if (runtimePaths.Count > 0)
                        {
                            endIndex += runtimePaths.Last().Length + 1;
                            runtimePaths.RemoveAt(runtimePaths.Count - 1);
                        }
                    }
                    string newPath0 = path.Substring(startIndex);
                    if (newPath0.StartsWith("parent["))
                    {
                        newPath0 = newPath0.Substring(6);
                        if (runtimePaths.Count > 0)
                        {
                            endIndex += runtimePaths.Last().Length + 1;
                            runtimePaths.RemoveAt(runtimePaths.Count - 1);
                        }
                    }
                    else
                    {
                        newPath0 = "." + newPath0;
                    }
                    string newPath1 = cursor.Length >= endIndex ? cursor.Substring(0, cursor.Length - endIndex) : string.Empty;
                    string newPath2 = newPath0;
                    if (newPath1.Length == 0 || newPath2.Length == 0)
                        return null;
                    path = newPath1 + newPath2;
                    data = runtimedata.Data;
                }
            }
            #endregion

            string[] paths = path.Split('.');
            var prefix = paths[0];
            var keys = prefix.Split('[');
            var key = keys.Length > 1 ? keys[0] : prefix;
            var result = string.IsNullOrWhiteSpace(key) ? data : (data as JObject).GetJsonObjectValue(key);
            if (result == null)
            {
                result = LinqHelper.GetLinqData(key, data as JObject);
            }
            else if (keys.Length > 1)
            {
                for (int i = 1; i < keys.Length; i++)
                {
                    if (result.Type != JTokenType.Array || !result.HasValues)
                        return null;
                    var paramArray = result as JArray;
                    int index = -1;
                    string num = keys[i].TrimEnd(']');
                    if (num.Equals("n", StringComparison.OrdinalIgnoreCase))
                        index = paramArray.Count - 1;
                    else if (!int.TryParse(num, out index))
                        index = -1;

                    if (index < 0 || paramArray.Count <= index)
                        return null;
                    result = paramArray[index];
                }
            }

            if (paths.Length > 1)
            {
                return GetValueByPath(path.Substring(prefix.Length + 1), result, false);
            }
            else
            {
                return result;
            }
        }

        public static JToken ParseObjectValues(JToken obj, JObject dataset, bool keepEmptyValue = true)
        {
            if (obj == null || (obj.Type != JTokenType.Array && obj.Type != JTokenType.Object))
                return obj;

            if (obj.Type == JTokenType.Array)
            {
                JArray result = obj as JArray;
                foreach (var item in result)
                {
                    ParseObjectValues(item, dataset, keepEmptyValue);
                }
                return result;
            }
            else
            {
                JObject result = obj as JObject;

                List<string> emptyKeys = new List<string>();
                foreach (var item in result)
                {
                    var value = result[item.Key];
                    if (value != null && value.Type == JTokenType.String && value.ToString().StartsWith("@"))
                    {
                        value = GetParameter(value.ToString().TrimStart('@'), dataset).Evaluate();
                    }

                    if (value == null || value.Type == JTokenType.Null || value.Type == JTokenType.Undefined || (value.Type == JTokenType.String && string.IsNullOrWhiteSpace(value.ToString())))
                    {
                        emptyKeys.Add(item.Key);
                        continue;
                    }
                    result[item.Key] = ParseObjectValues(value, dataset, keepEmptyValue);
                }

                if (!keepEmptyValue && emptyKeys.Count > 0)
                {
                    foreach (var item in emptyKeys)
                    {
                        result.Remove(item);
                    }
                }

                return result;
            }
        }

        public static ICache GetCacheAdapter(string connid, JObject config)
        {
            if (config == null)
                throw new ConfigException("constant.connection.{0} is null or not an object", connid);

            CacheType cacheType = CacheType.LOCAL;
            string connType = config.GetJsonObjectStringValue("type");
            if (string.IsNullOrWhiteSpace(connType))
                throw new ConfigException("constant.connection.{0}.type is null or empty", connid);

            if (!Enum.TryParse(connType.ToUpper(), out cacheType))
                throw new ConfigException("constant.connection.{0}.type={1} is not a supported cache type", connid, connType);

            string connectionStr = null;
            if (cacheType != CacheType.LOCAL)
            {
                connectionStr = config.GetJsonObjectStringValue("connectionString");
                if (string.IsNullOrWhiteSpace(connectionStr))
                    throw new ConfigException("constant.connection.{0}.connectionString is null or empty", connid);
            }

            string cacheAdapter = config.GetJsonObjectStringValue("cacheadapter");
            
            string connectionType = cacheType.ToString().ToLower();
            if (string.IsNullOrWhiteSpace(cacheAdapter))
            {
                string dllName = System.Reflection.Assembly.GetExecutingAssembly().FullName.Split(',')[0];
                string classPath = cacheType == CacheType.LOCAL ? dllName : dllName.Replace(".Base", ".Cache." + connectionType);
                cacheAdapter = string.Format("{0}.CacheHelper,{1}", classPath, cacheType == CacheType.LOCAL ? dllName : classPath);
            }
            var sType = Type.GetType(cacheAdapter, false, true);
            if (sType == null)
                throw ExceptionHelper.GenException(ErrorCode.E000000003, connectionType);

            var sArgs = new List<object>();
            if (!string.IsNullOrWhiteSpace(connectionStr))
                sArgs.Add(connectionStr);
            foreach (var item in config)
            {
                if (item.Key.Equals("type", StringComparison.OrdinalIgnoreCase) ||
                    item.Key.Equals("cacheadapter", StringComparison.OrdinalIgnoreCase) ||
                    item.Key.Equals("connectionstring", StringComparison.OrdinalIgnoreCase) || item.Value is JValue == false)
                    continue;
                sArgs.Add(((JValue)item.Value).Value);
            }
            var sObject = Activator.CreateInstance(sType, sArgs.ToArray());
            if (sObject == null || !(sObject is ICache))
                throw ExceptionHelper.GenException(ErrorCode.E000000003, connectionType);

            return (ICache)sObject;
        }

        public static DBBase GetDBAdapter(string connid, JObject config, string isolation, bool isTrans = true, bool autoDispose = true)
        {
            if (config == null)
                throw new ConfigException("constant.connection.{0} is null or not an object", connid);

            DatabaseType dbType = DatabaseType.MSSQL;
            string connType = config.GetJsonObjectStringValue("type");
            if (string.IsNullOrWhiteSpace(connType))
                throw new ConfigException("constant.connection.{0}.type is null or empty", connid);

            if (!Enum.TryParse(connType.ToUpper(), out dbType))
                throw new ConfigException("constant.connection.{0}.type={1} is not a supported db type", connid, connType);

            string connectionStr = config.GetJsonObjectStringValue("connectionString");
            if (string.IsNullOrWhiteSpace(connectionStr))
                throw new ConfigException("constant.connection.{0}.connectionString is null or empty", connid);

            string dbAdapter = config.GetJsonObjectStringValue("dbadapter");

            string connectionType = dbType.ToString().ToLower();
            if (string.IsNullOrWhiteSpace(dbAdapter))
                dbAdapter = string.Format("{0}.DBHelper,{0}", System.Reflection.Assembly.GetExecutingAssembly().FullName.Split(',')[0].Replace(".Base", ".Data."+ connectionType));
            var sType = Type.GetType(dbAdapter, false, true);
            if (sType == null)
                throw ExceptionHelper.GenException(ErrorCode.E000000004, connectionType);

            var isolationLevel = GetIsolationLevel(isolation);
            var sArgs = new List<object>() { connectionStr, isTrans, autoDispose };
            if (isolationLevel.HasValue)
                sArgs.Add(isolationLevel.Value);
            var sObject = Activator.CreateInstance(sType, sArgs.ToArray());
            if (sObject == null || !(sObject is DBBase))
                throw ExceptionHelper.GenException(ErrorCode.E000000004, connectionType);

            return (DBBase)sObject;
        }

        public static object GetJSAarguments(JToken arguments)
        {
            if (arguments == null || arguments.Type == JTokenType.Null || arguments.Type == JTokenType.Undefined)
                return null;
            else if (arguments is JValue)
                return ((JValue)arguments).Value;
            else if (arguments is JObject)
            {
                var obj = new DynamicObject();
                foreach (var item in arguments as JObject)
                {
                    obj.Add(item.Key, GetJSAarguments(item.Value));
                }
                return obj;
            }
            else if (arguments is JArray)
            {
                DynamicObjects<object> paramList = new DynamicObjects<object>();
                foreach (var arg in arguments as JArray)
                {
                    paramList.Add(GetJSAarguments(arg));
                }
                return paramList;
            }
            return null;
        }

        /// <summary>
        /// 将JSON模板解析为具体实体
        /// </summary>
        /// <param name="config">JSON模板</param>
        /// <param name="dataset">数据集合</param>
        /// <param name="isCmd">是否SQL命令实体</param>
        /// <param name="path">实体解析路径</param>
        /// <returns>执行结果</returns>
        public static JToken ParseObject(JToken config, JObject dataset, bool isCmd = false)
        {
            if (config == null)
                return config;
            if ((config.Type != JTokenType.Object && config.Type != JTokenType.Array) || !config.HasValues)
            {
                if (config.Type == JTokenType.String)
                {
                    string path = config.ToString();
                    if (path.Length == 0)
                        return config;
                    if (path.StartsWith("@"))//value是一个@参数，解析后再返回
                    {
                        return GetParameter(path.TrimStart('@'), dataset).Evaluate();
                    }
                    else
                    {
                        var function = GetFunction(path, dataset);
                        if (function != null)
                            return function.Execute(JValue.CreateUndefined());
                    }
                }
                return config;
            }

            JToken result = null;
            if (config.Type == JTokenType.Array)
                result = new JArray();
            else
                result = new JObject();

            var runtimedata = RuntimeData.GetData(dataset, result);
            var cursor = runtimedata.Cursor;

            if (config.Type == JTokenType.Array)
            {
                int index = 0;
                var results = result as JArray;
                foreach (var node in config as JArray)
                {
                    if (node.Type != JTokenType.Object)
                    {
                        runtimedata.SetCursor(string.Format("{0}[{1}]", cursor, index));
                        var value = ParseObject(node, dataset, isCmd);
                        if (results.Count - 1 < index) //如果小于index，表示还没添加，否则在上一步递归中生成runtimedata的时候已经添加过了
                            results.Add(value);
                        index++;
                        continue;
                    }
                    var containAtParam = false;
                    var itemResults = new JArray();
                    foreach (var item in node as JObject)
                    {
                        if (!item.Key.StartsWith("@"))
                            continue;
                        containAtParam = true;
                        runtimedata.SetCursor(string.Format("{0}[{1}].any", cursor, index));
                        JToken temp = ParseObject(item.Key, dataset, isCmd);
                        JToken data = SpreadData(item.Value, temp, dataset, isCmd, cursor, index);
                        MergeData(itemResults, data);
                    }

                    if (!containAtParam)
                    {
                        itemResults.Add(new JObject());
                    }

                    foreach (var item in itemResults)
                    {
                        results.Add(item);
                    }

                    foreach (var item in node as JObject)
                    {
                        if (item.Key.StartsWith("@"))
                            continue;
                        JToken value = item.Value;
                        if (isCmd && TryGetSqlKeywordValue(item.Key, ref value)) //特殊sql子句（if, from, where, 用于操作DB时控制逻辑）
                        {
                            for (var i = index; i < results.Count; i++)
                                results[i][item.Key] = value;
                            continue;
                        }

                        for (var i = index; i < results.Count; i++)
                        {
                            runtimedata.SetCursor(string.Format("{0}[{1}].{2}", cursor, i, item.Key));
                            results[i][item.Key] = ParseObject(value, dataset, isCmd);
                        }
                    }
                    index += itemResults.Count;
                }
            }
            else
            {
                foreach (var item in config as JObject)
                {
                    if (!item.Key.StartsWith("@"))
                        continue;
                    runtimedata.SetCursor(string.Format("{0}.any", cursor));
                    JToken temp = ParseObject(item.Key, dataset, isCmd);
                    JToken data = SpreadData(item.Value, temp, dataset, isCmd, cursor);
                    MergeData(result, data);
                }

                foreach (var item in config as JObject)
                {
                    if (item.Key.StartsWith("@"))
                        continue;
                    JToken value = item.Value;
                    if (isCmd && TryGetSqlKeywordValue(item.Key, ref value)) //特殊sql子句（if, from, where, 用于操作DB时控制逻辑）
                    {
                        result[item.Key] = value;
                        continue;
                    }
                    runtimedata.SetCursor(string.Format("{0}.{1}", cursor, item.Key));
                    result[item.Key] = ParseObject(value, dataset, isCmd);
                }
            }

            runtimedata.Clear(); //将要跳出该方法时，删除运行时data

            if (!result.HasValues && result.Type == JTokenType.Object)
                return null;

            return result;
        }

        private static JToken SpreadData(JToken config, JToken source, JObject dataset, bool isCmd, string cursor, int? index = null)
        {
            if (source == null || !source.HasValues || (source.Type != JTokenType.Object && source.Type != JTokenType.Array))
                return null;
            var rsArray = new JArray();
            var dsArray = new JArray();
            if (source.Type == JTokenType.Object)
                dsArray.Add(source);
            else if (source.Type == JTokenType.Array)
                dsArray = source as JArray;
            if (config == null || !config.HasValues || config.Type == JTokenType.Array)
            {
                foreach (var item in dsArray)
                {
                    if (item.Type != JTokenType.Object)
                        continue;
                    var obj = item as JObject;
                    if (config == null || !config.HasValues)
                    {
                        rsArray.Add(obj);
                        continue;
                    }
                    var newObj = new JObject();
                    foreach (var cfg in config as JArray)
                    {
                        if (cfg.Type != JTokenType.String)
                            continue;
                        var column = cfg.ToString();
                        var functions = GetFunctions(ref column, dataset);
                        if (string.IsNullOrWhiteSpace(column))
                            continue;
                        var exists = false;
                        foreach (var i in obj)
                        {
                            if (i.Key.Equals(column, StringComparison.OrdinalIgnoreCase))
                            {
                                newObj.SetJsonObjectValue(column, functions.Execute(i.Value));
                                exists = true;
                                break;
                            }
                        }
                        if (!exists)
                        {
                            newObj.SetJsonObjectValue(column, null);
                        }
                    }
                    rsArray.Add(newObj);
                }
            }
            else if (config.Type == JTokenType.Object)
            {
                JObject ds = new JObject();
                if (dataset != null)
                {
                    foreach (var item in dataset)
                    {
                        if (item.Key.Equals(RuntimeData.RuntimeRoot, StringComparison.OrdinalIgnoreCase))
                            ds.Add(item.Key, item.Value.DeepClone());
                        else
                            ds.Add(item.Key, item.Value);
                    }
                }
                RuntimeData runtimedata = RuntimeData.GetData(ds, null);

                int idx = 0;
                foreach (var item in dsArray)
                {
                    if (item.Type != JTokenType.Object)
                        continue;
                    var obj = item as JObject;
                    var newCursor = index.HasValue ? string.Format("{0}[{1}]", cursor, index + idx) : cursor;
                    if (runtimedata != null)
                    {
                        //var cloneObj = obj.DeepClone();
                        //foreach (var i in obj)
                        //    cloneObj["super_" + i.Key] = i.Value;
                        runtimedata.SetCursor(newCursor);
                        runtimedata = RuntimeData.GetData(ds, obj);
                    }
                    var newObj = new JObject();
                    foreach (var cfg in config as JObject)
                    {
                        if (!cfg.Key.StartsWith("@"))
                            continue;
                        runtimedata?.SetCursor(string.Format("{0}.any", newCursor));
                        JToken temp = ParseObject(cfg.Key, ds, isCmd);
                        JToken data = SpreadData(cfg.Value, temp, ds, isCmd, newCursor);
                        MergeData(newObj, data);
                    }

                    foreach (var cfg in config as JObject)
                    {
                        if (cfg.Key.StartsWith("@"))
                            continue;
                        JToken value = cfg.Value;
                        if (isCmd && TryGetSqlKeywordValue(cfg.Key, ref value)) //特殊sql子句（if, from, where, 用于操作DB时控制逻辑）
                        {
                            newObj[cfg.Key] = value;
                            continue;
                        }
                        runtimedata?.SetCursor(string.Format("{0}.{1}", newCursor, cfg.Key));
                        newObj[cfg.Key] = ParseObject(value, ds, isCmd);
                    }

                    rsArray.Add(newObj);
                    idx++;
                }
            }
            return source.Type == JTokenType.Array ? rsArray : source.Type == JTokenType.Object && rsArray.HasValues ? rsArray[0] : null;
        }

        public static void MergeData(JToken target, JToken data)
        {
            if (target == null || (target.Type != JTokenType.Object && target.Type != JTokenType.Array))
                return;
            if (data == null || !data.HasValues || (data.Type != JTokenType.Object && data.Type != JTokenType.Array))
                return;


            if (target.Type == JTokenType.Object)
            {
                var r = target as JObject;
                var d = data.Type == JTokenType.Object ? data as JObject : data.FirstOrDefault() as JObject;
                if (d != null)
                {
                    foreach (var i in d)
                    {
                        if (!r.ContainsProperty(i.Key))
                            r.Add(i.Key, i.Value);
                    }
                }
            }
            else if (target.Type == JTokenType.Array)
            {
                var targetArray = target as JArray;
                var dataArray = data.Type == JTokenType.Object ? new JArray() { data } : data as JArray;
                if (targetArray.HasValues)
                {
                    for (int rindex = 0; rindex < targetArray.Count; rindex++)
                    {
                        for (int dindex = 0; dindex < dataArray.Count; dindex++)
                        {
                            if (rindex == dindex || data.Type == JTokenType.Object) //如果data是Array，按顺序合并到target的对应项，如果data是Object，分别合并到target每一项
                            {
                                var r = targetArray[rindex] as JObject;
                                var d = dataArray[dindex] as JObject;
                                if (d != null)
                                {
                                    foreach (var i in d)
                                    {
                                        if (!r.ContainsProperty(i.Key))
                                            r.Add(i.Key, i.Value);
                                    }
                                }
                                break;
                            }
                        }
                    }
                }
                else
                {
                    foreach (var ditem in dataArray)
                    {
                        if (ditem is JObject)
                            targetArray.Add(ditem);
                    }
                }
            }
        }

        public static JObject FilterConvert(JToken filter)
        {
            if (filter == null || (filter.Type != JTokenType.Array && filter.Type != JTokenType.Object))
                return null;
            JObject filterObject = filter.Type == JTokenType.Object ? filter as JObject : new JObject() { { "groups", filter } };
            if (filterObject == null)
                return null;
            if (filterObject.ContainsProperty("field") || filterObject.ContainsProperty("value") || filterObject.ContainsProperty("groups") || filterObject.ContainsProperty("conditions"))
            {
                if (!filterObject.ContainsProperty("groups") && filterObject.ContainsProperty("conditions")) //兼容历史版本
                    filterObject.Add("groups", filterObject.GetJsonObjectValue("conditions"));
                return filterObject;
            }
            JArray list = new JArray();
            foreach (var item in filterObject)
            {
                if (item.Value is JValue)
                    list.Add(new JObject() { { "field", item.Key }, { "value", item.Value } });
                else if (item.Value.Type == JTokenType.Array)
                {
                    JArray groups = new JArray();
                    foreach (var valueItem in item.Value as JArray)
                    {
                        if (valueItem is JValue)
                            groups.Add(new JObject() { { "value", valueItem } });
                        else
                        {
                            var subFilter = FilterConvert(valueItem);
                            if (subFilter != null)
                                groups.Add(subFilter);
                        }
                    }
                    if (groups.HasValues)
                        list.Add(new JObject() { { "field", item.Key }, { "mode", "in" }, { "groups", groups } });
                }
                else
                {
                    var subFilter = FilterConvert(item.Value);
                    if (subFilter != null)
                    {
                        var field = subFilter.GetJsonObjectStringValue("field");
                        if (string.IsNullOrWhiteSpace(field))
                            subFilter.SetJsonObjectValue("field", item.Key);
                        list.Add(subFilter);
                    }
                }
            }
            if (!list.HasValues)
                return null;
            return new JObject() { { "groups", list } };
        }

        private static bool TryGetSqlKeywordValue(string key, ref JToken value)
        {
            if (string.IsNullOrWhiteSpace(key))
                return false;
            key = key.ToLower();
            if (!SqlKeywords.Contains(key))
                return false;
            if (key == "identity" && value?.Type == JTokenType.String && !value.ToString().StartsWith("@"))
                value = "@" + value.ToString();
            return true;
        }
    }
}